import random
from micrograd.engine import Value
#这里好像是在自定义简化的torch.nn
#这是一个基类，类似于接口提供一个神经网络需要的规范
class Module:

    def zero_grad(self):
        for p in self.parameters():
            p.grad = 0

    def parameters(self):
        return []
#这是一个自定义的神经元，是神经网络基本的单位
#nin代表上一层神经元和这个神经元联系的数量
#nonlin参数代表是否对这个神经元的输出进行激活函数处理，这里设置为true
class Neuron(Module):

    def __init__(self, nin, nonlin=True):
        self.w = [Value(random.uniform(-1,1)) for _ in range(nin)] #这是随机生成了那些连接的权重，连接的权重本来就是随机生成的因为可以通过后续的学习来更改
        self.b = Value(0)   #这里用的都是value只要是用value的值就可以算梯度，有梯度就是可以学习的参数
        self.nonlin = nonlin
#这个__call__方法可以使得类像一个函数一样被调用，类似于函数对象，比如一个该类的实例A，A（a，b）就相当于A.__call__(a,b)
    def __call__(self, x):
        #zip(self.w, x)将相同索引位置上的元素组成元组
        act = sum((wi*xi for wi,xi in zip(self.w, x)), self.b) #这里返回的act实际上也是一个value对象，因为wi和xi还有b均是value对象
        return act.relu() if self.nonlin else act

    def parameters(self):
        return self.w + [self.b] #这样操作会使得两个列表像string一样拼接

    def __repr__(self):
        return f"{'ReLU' if self.nonlin else 'Linear'}Neuron({len(self.w)})"
#这个类对应的是网络中的一层
class Layer(Module):
#nin：此层的输入   nout：此层的输出
    def __init__(self, nin, nout, **kwargs):
        self.neurons = [Neuron(nin, **kwargs) for _ in range(nout)] #这应该是在构建一层的神经元
#对于一层的输入是一个列表
    def __call__(self, x):
        out = [n(x) for n in self.neurons]
        return out[0] if len(out) == 1 else out

    def parameters(self):
        return [p for n in self.neurons for p in n.parameters()]

    def __repr__(self):
        return f"Layer of [{', '.join(str(n) for n in self.neurons)}]"
#下面这个代码定义了一个多层感知机，使用自制的微分系统视线

class MLP(Module):

    def __init__(self, nin, nouts):
        sz = [nin] + nouts
        self.layers = [Layer(sz[i], sz[i+1], nonlin=i!=len(nouts)-1) for i in range(len(nouts))]

    def __call__(self, x):
        for layer in self.layers:
            x = layer(x)
        return x

    def parameters(self):
        return [p for layer in self.layers for p in layer.parameters()]

    def __repr__(self):
        return f"MLP of [{', '.join(str(layer) for layer in self.layers)}]"
